<?php


if (!PHP5 || !App::import('Lib', 'LazyModel.LazyModel')) {
	class LazyModel extends Model {} // dummy
}


class AppModel extends LazyModel {

/******************************************************************************/

//var $useDbConfig = 'hosting';

var $recursive = -1;

/******************************************************************************/

//var $primaryKey = 'id';

var $displayImage = 'thumbnail';
var $displayField = 'caption';

//var $order = array('id' => 'ASC');

var $fieldInputSchema = array();

var $scopeConditions = array();

var $paginateLimit = FALSE;

// https://trac.cakephp.org/ticket/2056
function afterFind($results, $primary = FALSE) {
	$results = parent::afterFind($results, $primary);
	if (!$primary && $this->Behaviors->enabled('Uploadable')) {
		$results = $this->Behaviors->Uploadable->afterFind($this, $results, FALSE);
	}
	// deprecated
	if (!$primary && $this->Behaviors->enabled('UploadAudio')) {
		$results = $this->Behaviors->UploadAudio->afterFind($this, $results, FALSE);
	}
	if (!$primary && $this->Behaviors->enabled('UploadFile')) {
		$results = $this->Behaviors->UploadFile->afterFind($this, $results, FALSE);
	}
	if (!$primary && $this->Behaviors->enabled('UploadImage')) {
		$results = $this->Behaviors->UploadImage->afterFind($this, $results, FALSE);
	}
	if (!$primary && $this->Behaviors->enabled('UploadVideo')) {
		$results = $this->Behaviors->UploadVideo->afterFind($this, $results, FALSE);
	}
	return $results;
}

/******************************************************************************/

function isAutomated() {
	return $this->Behaviors->attached('Automate');
}

function hasSlug() {
	return $this->hasField('slug');
}

function escapeField($field = NULL, $alias = NULL) {
	return (bool)preg_match('~^(?:`?' . preg_quote($this->name) . '`?\.)?`?([a-z0-9_]+)`?$~i', $field, $match) ? parent::escapeField($match[1], $alias) : parent::escapeField($field, $alias);
}

function unescapeField($escaped_field) {
	return (bool)preg_match('~^(?:`?' . preg_quote($this->name) . '`?\.)?`?([a-z0-9_]+)`?$~i', $escaped_field, $match) ? $match[1] : FALSE;
}

/******************************************************************************/

function findById($id) {
	return $this->find('byid', array('value' => $id));
}

function findByIdOrSlug($id_or_slug) {
	return $this->hasSlug() ? $this->find('byidorslug', array('value' => $id_or_slug)) : $this->findById($id_or_slug);
}

function getSlugById($id) {
	return $this->hasSlug() ? $this->find('byid', array('value' => $id, 'return' => $this->escapeField('slug'))) : NULL;
}

function getIdByIdOrSlug($id_or_slug) {
	return $this->hasSlug() ? $this->find('byidorslug', array('value' => $id_or_slug, 'return' => $this->escapeField($this->primaryKey))) : NULL;
}

/******************************************************************************/

function firstBeforeFind($query) {
	return $query;
}

/******************************************************************************/

function find($conditions = NULL, $fields = array(), $order = NULL, $recursive = NULL) {
	$this->_findMethods['byid'] = TRUE;
	$this->_findMethods['byidorslug'] = TRUE;
	$this->_findMethods['myneighbors'] = TRUE;
	$this->_findMethods['myneighborsdate'] = TRUE;
	$this->_findMethods['possibleparents'] = TRUE;
	$this->_findMethods['pathnodes'] = TRUE;
	$this->_findMethods['parentnode'] = TRUE;
	$this->_findMethods['siblings'] = TRUE;
	$this->_findMethods['children'] = TRUE;
	if (is_string($conditions) && array_key_exists($conditions, $this->_findMethods) && is_array($fields)) $fields = $this->firstBeforeFind($fields);
	return parent::find($conditions, $fields, $order, $recursive);
}

/******************************************************************************/

function _findFirst($state, $query, $results = array()) {
	if ($state === 'before') {
		if (array_key_exists('return', $query)) $query['fields'] = $query['return'] = $this->escapeField($query['return']);
		return parent::_findFirst($state, $query, $results);
	} elseif (($state === 'after') && array_key_exists(0, $results)) {
		if (array_key_exists('return', $query)) {
			$unescaped_field = $this->unescapeField($query['return']);
			return isset($results[0][$this->name][$unescaped_field]) ? $results[0][$this->name][$unescaped_field] : NULL;
		}
		return $results[0];
	}
	return FALSE;
}

function _findById($state, $query, $results = array()) {
	if ($state === 'before') {
		if (!array_key_exists('id_field', $query)) $query['id_field'] = $this->escapeField($this->primaryKey);
		if (!array_key_exists('value', $query)) $query['value'] = $this->id;
		$query['conditions'] = array($query['id_field'] => $query['value']);
		return $this->_findFirst($state, $query, $results);
	} elseif (($state === 'after') && array_key_exists(0, $results)) {
		return $this->_findFirst($state, $query, $results);
	}
	return FALSE;
}

function _findByIdOrSlug($state, $query, $results = array()) {
	if ($state === 'before') {
		if (!array_key_exists('id_field', $query)) $query['id_field'] = $this->escapeField($this->primaryKey);
		if (!array_key_exists('slug_field', $query)) $query['slug_field'] = $this->escapeField('slug');
		if (!array_key_exists('value', $query)) $query['value'] = $this->id;
		$query['conditions'] = array('or' => array($query['id_field'] => $query['value'], $query['slug_field'] => $query['value']));
		return $this->_findFirst($state, $query, $results);
	} elseif (($state === 'after') && array_key_exists(0, $results)) {
		return $this->_findFirst($state, $query, $results);
	}
	return FALSE;
}

/******************************************************************************/

var $neighborhood = array();

function prepareOrderRules($order = NULL) {
	$order_rules = array();
	$order = is_null($order) ? (array)$this->order : (array)$order;
	foreach ($order as $name => $asc_desc) {
		$order_rules[$this->escapeField($name)] = strtoupper($asc_desc);
		// improve !!!
	}
	return $order_rules;
}

function _findMyNeighbors($state, $query, $results = array()) {
	if ($state === 'before') {
		if (!isset($query['find_type'])) {
			$query['find_type'] = 'first';
		}
		if (!isset($query['field'])) {
			$query['field'] = $this->escapeField($this->primaryKey);
		}
		if (!isset($query['value'])) {
			$query['value'] = $this->id;
		}
		if (!isset($query['include_self'])) {
			$query['include_self'] = FALSE;
		}
		if (!isset($query['neighborhood'])) {
			$query['neighborhood'] = array_map(array($this, 'escapeField'), $this->neighborhood);
		}
		$query['order_rules'] = $this->prepareOrderRules($query['order']);
		$query['order_fields'] = array_map(array($this, 'escapeField'), array_keys($query['order_rules']));
		$before = array_fill_keys(array_keys($query), NULL);
		$before['conditions'] = array($query['field'] => $query['value']);
		$before['fields'] = array_merge($query['neighborhood'], $query['order_fields']);
		$before['limit'] = 1;
		$before['after'] = $query;
		return $before;
	} elseif (($state === 'after') && isset($results[0][$this->name]) && is_array($results[0][$this->name])) {
		$after = $query['after'];
		$after_prev = $after_self = $after_next = $after;
		$after_prev['conditions'] = array($after['field'] . ' <>' => $after['value']);
		$after_self['conditions'] = array($after['field'] => $after['value']);
		$after_next['conditions'] = array($after['field'] . ' <>' => $after['value']);
		$after_prev['order'] = $after_self['order'] = $after_next['order'] = NULL;
		foreach ($results[0][$this->name] as $field_name => $field_value) {
			$escaped_field = $this->escapeField($field_name);
			if (in_array($escaped_field, $after['neighborhood'])) {
				$after_prev['conditions'][$this->escapeField($field_name)] = $field_value;
				$after_next['conditions'][$this->escapeField($field_name)] = $field_value;
			} elseif (in_array($escaped_field, $after['order_fields']) && isset($after['order_rules'][$escaped_field])) {
				$asc = strtolower($after['order_rules'][$escaped_field]) !== 'desc';
				if (array_search($escaped_field, $after['order_fields']) === 0) {
					$after_prev['conditions'][$this->escapeField($field_name) . ($asc ? ' >' : ' <')] = $field_value;
					$after_next['conditions'][$this->escapeField($field_name) . (!$asc ? ' >' : ' <')] = $field_value;
				}
				$after_prev['order'][$this->escapeField($field_name)] = $asc ? 'ASC' : 'DESC';
				$after_next['order'][$this->escapeField($field_name)] = !$asc ? 'ASC' : 'DESC';
			}
		}
		$neighbors = array();
		$neighbors['prev'] = $this->find($after['find_type'], $after_prev);
		if ($after['include_self']) {
			$neighbors['self'] = $this->find($after['find_type'], $after_self);
		}
		$neighbors['next'] = $this->find($after['find_type'], $after_next);
		return $neighbors;
	}
	return FALSE;
}

function _findMyNeighborsDate($state, $query, $results = array()) {
	if ($state === 'before') {
		return $this->_findMyNeighbors($state, $query, $results);
	} elseif (($state === 'after') && isset($results[0][$this->name]) && is_array($results[0][$this->name])) {
		$neighbors = $this->_findMyNeighbors($state, $query, $results);
		// fixing
		$date_after = $query['after'];
		if (isset($date_after['order_fields'][0]) && ($this->getColumnType($date_after['order_fields'][0]) === 'date') && (count($date_after['order_rules']) >= 2)) {
			$date_after['include_self'] = FALSE;
			$date_after['neighborhood'][] = $date_after['order_fields'][0];
			$date_after['order'] = array_slice($date_after['order_rules'], 1);
			unset($date_after['order_rules'], $date_after['order_fields']);
			$date_neighbors = $this->find('myneighbors', $date_after);
			if (isset($date_neighbors['prev']) && is_array($date_neighbors['prev']) && !empty($date_neighbors['prev'])) {
				$neighbors['prev'] = $date_neighbors['prev'];
			}
			if (isset($date_neighbors['next']) && is_array($date_neighbors['next']) && !empty($date_neighbors['next'])) {
				$neighbors['next'] = $date_neighbors['next'];
			}
		}
		return $neighbors;
	}
	return FALSE;
}

/******************************************************************************/

function _findPossibleParents($state, $query, $results = array()) {
	if ($this->Behaviors->attached('Tree')) {
		$cfg = $this->Behaviors->Tree->settings[$this->name];
		if ($state === 'before') {
			if (!array_key_exists('find_type', $query)) $query['find_type'] = 'threaded';
			if (!array_key_exists('field', $query)) $query['field'] = $this->escapeField($this->primaryKey);
			if (!array_key_exists('value', $query)) $query['value'] = $this->id;
			if (!array_key_exists('include_self', $query)) $query['include_self'] = FALSE;
			$before = array_fill_keys(array_keys($query), NULL);
			$before['conditions'] = array($query['field'] => $query['value']);
			$before['fields'] = array($this->escapeField($this->primaryKey), $this->escapeField($cfg['parent']), $this->escapeField($cfg['left']), $this->escapeField($cfg['right']));
			$before['limit'] = 1;
			$before['after'] = $query;
			return $before;
		} elseif (($state === 'after') && array_key_exists(0, $results)) {
			$after = $query['after'];
			$after['conditions'] = array_merge((array)$after['conditions'], array('or' => array($this->escapeField($cfg['left']) . ($after['include_self'] ? ' <=' : ' <') => $results[0][$this->name][$cfg['left']], $this->escapeField($cfg['right']) . ($after['include_self'] ? ' >=' : ' >') => $results[0][$this->name][$cfg['right']])));
			$after['order'] = $this->escapeField($cfg['left']); // arguably
			return $this->find($after['find_type'], $after);
		}
	}
	return FALSE;
}

function _findPathNodes($state, $query, $results = array()) {
	if ($this->Behaviors->attached('Tree')) {
		$cfg = $this->Behaviors->Tree->settings[$this->name];
		if ($state === 'before') {
			if (!array_key_exists('find_type', $query)) $query['find_type'] = 'all';
			if (!array_key_exists('field', $query)) $query['field'] = $this->escapeField($this->primaryKey);
			if (!array_key_exists('value', $query)) $query['value'] = $this->id;
			if (!array_key_exists('include_self', $query)) $query['include_self'] = FALSE;
			$before = array_fill_keys(array_keys($query), NULL);
			$before['conditions'] = array($query['field'] => $query['value']);
			$before['fields'] = array($this->escapeField($this->primaryKey), $this->escapeField($cfg['parent']), $this->escapeField($cfg['left']), $this->escapeField($cfg['right']));
			$before['limit'] = 1;
			$before['after'] = $query;
			return $before;
		} elseif (($state === 'after') && array_key_exists(0, $results)) {
			$after = $query['after'];
			$after['conditions'] = array_merge((array)$after['conditions'], array($this->escapeField($cfg['left']) . ($after['include_self'] ? ' <=' : ' <') => $results[0][$this->name][$cfg['left']], $this->escapeField($cfg['right']) . ($after['include_self'] ? ' >=' : ' >') => $results[0][$this->name][$cfg['right']]));
			$after['order'] = $this->escapeField($cfg['left']); // arguably
			return $this->find($after['find_type'], $after);
		}
	}
	return FALSE;
}

function _findParentNode($state, $query, $results = array()) {
	if ($this->Behaviors->attached('Tree')) {
		$cfg = $this->Behaviors->Tree->settings[$this->name];
		if ($state === 'before') {
			if (!array_key_exists('find_type', $query)) $query['find_type'] = 'first';
			if (!array_key_exists('field', $query)) $query['field'] = $this->escapeField($this->primaryKey);
			if (!array_key_exists('value', $query)) $query['value'] = $this->id;
			//if (!array_key_exists('include_self', $query)) $query['include_self'] = FALSE;
			$before = array_fill_keys(array_keys($query), NULL);
			$before['conditions'] = array($query['field'] => $query['value']);
			$before['fields'] = array($this->escapeField($this->primaryKey), $this->escapeField($cfg['parent']), $this->escapeField($cfg['left']), $this->escapeField($cfg['right']));
			$before['limit'] = 1;
			$before['after'] = $query;
			return $before;
		} elseif (($state === 'after') && array_key_exists(0, $results)) {
			$after = $query['after'];
			$after['conditions'] = array_merge((array)$after['conditions'], array($this->escapeField($this->primaryKey) => $results[0][$this->name][$cfg['parent']]));
			$after['order'] = $this->escapeField($cfg['left']); // arguably
			return $this->find($after['find_type'], $after);
		}
	}
	return FALSE;
}

function _findSiblings($state, $query, $results = array()) {
	if ($this->Behaviors->attached('Tree')) {
		$cfg = $this->Behaviors->Tree->settings[$this->name];
		if ($state === 'before') {
			if (!array_key_exists('find_type', $query)) $query['find_type'] = 'all';
			if (!array_key_exists('field', $query)) $query['field'] = $this->escapeField($this->primaryKey);
			if (!array_key_exists('value', $query)) $query['value'] = $this->id;
			if (!array_key_exists('include_self', $query)) $query['include_self'] = FALSE;
			$before = array_fill_keys(array_keys($query), NULL);
			$before['conditions'] = array($query['field'] => $query['value']);
			$before['fields'] = array($this->escapeField($this->primaryKey), $this->escapeField($cfg['parent']), $this->escapeField($cfg['left']), $this->escapeField($cfg['right']));
			$before['limit'] = 1;
			$before['after'] = $query;
			return $before;
		} elseif (($state === 'after') && array_key_exists(0, $results)) {
			$after = $query['after'];
			$after['conditions'] = array_merge((array)$after['conditions'], array($this->escapeField($cfg['parent']) => $results[0][$this->name][$cfg['parent']]));
			if (!$after['include_self']) $after['conditions'] = array_merge((array)$after['conditions'], array($this->escapeField($this->primaryKey) . ' <>' => $results[0][$this->name][$this->primaryKey]));
			$after['order'] = $this->escapeField($cfg['left']); // arguably
			return $this->find($after['find_type'], $after);
		}
	}
	return FALSE;
}

function _findChildren($state, $query, $results = array()) {
	if ($this->Behaviors->attached('Tree')) {
		$cfg = $this->Behaviors->Tree->settings[$this->name];
		if ($state === 'before') {
			if (!array_key_exists('find_type', $query)) $query['find_type'] = 'threaded';
			if (!array_key_exists('field', $query)) $query['field'] = $this->escapeField($this->primaryKey);
			if (!array_key_exists('value', $query)) $query['value'] = $this->id;
			if (!array_key_exists('include_self', $query)) $query['include_self'] = FALSE;
			$before = array_fill_keys(array_keys($query), NULL);
			$before['conditions'] = array($query['field'] => $query['value']);
			$before['fields'] = array($this->escapeField($this->primaryKey), $this->escapeField($cfg['parent']), $this->escapeField($cfg['left']), $this->escapeField($cfg['right']));
			$before['limit'] = 1;
			$before['after'] = $query;
			return $before;
		} elseif (($state === 'after') && array_key_exists(0, $results)) {
			$after = $query['after'];
			$after['conditions'] = array_merge((array)$after['conditions'], array($this->escapeField($cfg['left']) . ($after['include_self'] ? ' >=' : ' >') => $results[0][$this->name][$cfg['left']], $this->escapeField($cfg['right']) . ($after['include_self'] ? ' <=' : ' <') => $results[0][$this->name][$cfg['right']]));
			$after['order'] = $this->escapeField($cfg['left']); // arguably
			return $this->find($after['find_type'], $after);
		}
	}
	return FALSE;
}

/******************************************************************************/

var $stack = array();

function pushData() {
	return array_push($this->stack, array($this->id, $this->data));
}

function popData() {
	list($this->id, $this->data) = array_pop($this->stack);
	return $this->data;
}

function saveSafe($data = NULL, $validate = TRUE, $fieldList = array()) {
	$this->pushData();
	$result = $this->save($data, $validate, $fieldList);
	$this->popData();
	return $result;
}

function saveAllSafe($data = NULL, $options = array()) {
	$this->pushData();
	$result = $this->saveAll($data, $options);
	$this->popData();
	return $result;
}

function saveFieldSafe($name, $value, $validate = FALSE) {
	$this->pushData();
	$result = $this->saveField($name, $value, $validate);
	$this->popData();
	return $result;
}

/******************************************************************************/

function create($data = array(), $filterKey = FALSE) {
	$result = parent::create($data, $filterKey);
	if ($filterKey && isset($this->data[$this->name][$this->primaryKey])) unset($this->data[$this->name][$this->primaryKey]);
	return $result;
}

/******************************************************************************/

}


App::import('Model', 'AppProjectModel');
App::import('Model', array('GeneralModel', 'AttachmentModel')); // deprecated


?>